/**
 * Created by cher on 2016/6/21.
 * 本地数据库操作
 */
;(function(factory) {
    if (typeof define === 'function' && define.amd) {
        define(factory);
    } else {
        window.driver = factory();
    }
})(function() {
    "use strict";
    Array.prototype.contains = function (elem) {
        var s =  String.fromCharCode(2);
        var r = new RegExp(s + elem + s);
        return (r.test(s + this.join(s) + s));
    }

    if (!Array.prototype.indexOf) {
        Array.prototype.indexOf = function (elem) {
            for (var i = 0; i < this.length; i++) {
                if (i in this && this[i] === elem) {
                    return i;
                }
            }
            return -1;
        }
    }
    Date.parseDate = function(dateStr){
        var date = (0,eval)('new Date(' + dateStr.replace(/\d+(?=-[^-]+$)/,
                function (a) { return parseInt(a, 10) - 1; }).match(/\d+/g) + ')');
        date.setMonth(date.getMonth());
        return date;
    }
    Date.format = function(date, pattern){
        pattern = pattern || "yyyy-MM-dd HH:mm:ss SSS";
        return (date || new Date()).format(pattern);
    }
    Date.prototype.format=function(fmt) {
        var o = {
            "M+" : this.getMonth()+1, //月份
            "d+" : this.getDate(), //日
            "h+" : this.getHours()%12 == 0 ? 12 : this.getHours()%12, //小时
            "H+" : this.getHours(), //小时
            "m+" : this.getMinutes(), //分
            "s+" : this.getSeconds(), //秒
            "q+" : Math.floor((this.getMonth()+3)/3), //季度
            "S" : this.getMilliseconds() //毫秒
        };
        var week = {
            "0" : "/u65e5",
            "1" : "/u4e00",
            "2" : "/u4e8c",
            "3" : "/u4e09",
            "4" : "/u56db",
            "5" : "/u4e94",
            "6" : "/u516d"
        };
        if(/(y+)/.test(fmt)){
            fmt=fmt.replace(RegExp.$1, (this.getFullYear()+"").substr(4 - RegExp.$1.length));
        }
        if(/(E+)/.test(fmt)){
            fmt=fmt.replace(RegExp.$1, ((RegExp.$1.length>1) ? (RegExp.$1.length>2 ? "/u661f/u671f" : "/u5468") : "")+week[this.getDay()+""]);
        }
        for(var k in o){
            if(new RegExp("("+ k +")").test(fmt)){
                fmt = fmt.replace(RegExp.$1, (RegExp.$1.length==1) ? (o[k]) : (("00"+ o[k]).substr((""+ o[k]).length)));
            }
        }
        return fmt;
    }
    var clone = function(obj) {
        var o;
        if (typeof obj == "object") {
            if (obj === null) {
                o = null;
            } else {
                if (obj instanceof Array) {
                    o = [];
                    for (var i = 0, len = obj.length; i < len; i++) {
                        o.push(clone(obj[i]));
                    }
                } else {
                    o = {};
                    for (var j in obj) {
                        o[j] = clone(obj[j]);
                    }
                }
            }
        } else {
            o = obj;
        }
        return o;
    }
    var utils = {
        isFunction: function (obj) {
            return Object.prototype.toString.call(obj) === '[object Function]';
        },
        isUndefined: function (obj) {
            return Object.prototype.toString.call(obj) === '[object Undefined]';
        },
        isNotUndefined: function (obj) {
            return !this.isUndefined(obj);
        },
        isNull: function (obj) {
            return obj == null;
        },
        isNotNull: function (obj) {
            return !this.isNull(obj);
        },
        isBlank: function (obj) {
            return this.isNull(obj) || this.isUndefined(obj) || obj == "";
        },
        isEmpty: function (obj) {
            if (this.isObject(obj)) {
                for (var p in obj) {
                    if (this.isFunction(obj[p])) continue;
                    return true;
                }
            } else if (this.isArray(obj) || this.isString(obj)) {
                return obj.length == 0;
            }
            return false;
        },
        isArray: function (obj) {
            return Object.prototype.toString.call(obj) === '[object Array]';
        },
        isString: function (obj) {
            return Object.prototype.toString.call(obj) === '[object String]';
        },
        isObject: function (obj) {
            return Object.prototype.toString.call(obj) === '[object Object]';
        },
        isArguments: function (obj) {
            return Object.prototype.toString.call(obj) === '[object Arguments]';
        },
        nowMilliseconds: function () {
            return new Date().getTime();
        },
        each: function (obj, fn) {
            obj = obj || {}
            fn = fn || utils.noop
            if(obj.forEach) {
                obj.forEach(function(item, index, array){
                    if (fn.call(item, index, item, array) === false) return
                })
            } else {
                for (var p in obj) {
                    if (fn.call(obj[p], p, obj[p], obj) === false) break
                }
            }
        },
        extend: function () {
            var rs = arguments[0] = arguments[0] || {}
            if (arguments.length <= 1) {
                return rs
            } else {
                if (!utils.isObject(arguments[1])) {
                    rs = arguments[1]
                } else {
                    utils.each(arguments[1], function (k, v) {
                        if(v === null || undefined === v) return
                        !rs[k] && (rs[k] = {})
                        utils.isObject(v) ? (utils.extend(rs[k], v)) : (rs[k] = v)
                    })
                }
                for (var i = 2; i < arguments.length; i++) {
                    utils.extend(rs, arguments[i])
                }
            }
            return rs
        },
        noop: function () {
        }
    };

    /**
     * 事务的模式
     * READ：读
     * READ_WRITE：读写
     * VERION_CHANGE: 版本变更
     * @type {{READ: string, READ_WRITE: string, VERION_CHANGE: string}}
     */
    var TransactionMode = {READONLY:"readonly", READ_WRITE:"readwrite", READ_WRITE_FLUSH:"readwriteflush ", VERION_CHANGE:"verionchange"};

    /*
     * 游标循环的方向
     * CursorDirection.NEXT                     顺序循环
     * CursorDirection.NEXT_NO_DUPLICATE        顺序循环不重复.当取值有重复时，只获取一次。
     * CursorDirection.PREV                     倒序循环
     * CursorDirection.PREV_NO_DUPLICATE        倒序循环不重复.当取值有重复时，只获取一次。
     * @type {{NEXT: *, NEXT_NO_DUPLICATE: *, PREV: *, PREV_NO_DUPLICATE: *}}
     */
    window.CursorDirection = {NEXT:IDBCursor.NEXT, NEXT_NO_DUPLICATE:IDBCursor.NEXT_NO_DUPLICATE, PREV:IDBCursor.PREV, PREV_NO_DUPLICATE:IDBCursor.PREV_NO_DUPLICATE};


    /**
     *  IDBKeyRange All keys ≤ x 	IDBKeyRange.upperBound(x)
     *  All keys < x 	IDBKeyRange.upperBound(x, true)
     *  All keys ≥ y 	IDBKeyRange.lowerBound(y)
     *  All keys > y 	IDBKeyRange.lowerBound(y, true)
     *  All keys ≥ x && ≤ y 	IDBKeyRange.bound(x, y)
     *  All keys > x &&< y 	IDBKeyRange.bound(x, y, true, true)
     *  All keys > x && ≤ y 	IDBKeyRange.bound(x, y, true, false)
     *  All keys ≥ x &&< y 	IDBKeyRange.bound(x, y, false, true)
     *  The key = z 	IDBKeyRange.only(z)
     * @type
     * {{
     *      ltEq: Window.KeyRange.ltEq,
     *      lt: Window.KeyRange.lt,
     *      gtEq: Window.KeyRange.gtEq,
     *      gt: Window.KeyRange.gt,
     *      between: Window.KeyRange.between,
     *      openInterval: Window.KeyRange.openInterval,
     *      gtLtEq: Window.KeyRange.gtLtEq,
     *      gtEqLt: Window.KeyRange.gtEqLt,
     *      eq: Window.KeyRange.eq
     *  }}
     */
    window.KeyRange = {
        ltEq : function(x){return DBKeyRange.upperBound(x);},
        lt : function(x){return DBKeyRange.upperBound(x, true);},
        gtEq : function(x){return DBKeyRange.lowerBound(x);},
        gt : function(x){return DBKeyRange.lowerBound(x, true);},
        between : function(x, y){return DBKeyRange.bound(x, y);},
        gtLt : function(x, y){return DBKeyRange.bound(x, y, true, true);},
        gtLtEq : function(x, y){return DBKeyRange.bound(x, y, true, false);},
        gtEqLt : function(x, y){return DBKeyRange.bound(x, y, false, false);},
        eq : function(x){return DBKeyRange.only(x);},
        ALL_KEYS:"_ALL_KEYS"
    };

    /*****************************************Table********************************************/
    /**
     * 数据表对象
     * @param name 表名称
     * @param driver 驱动
     * @constructor
     */
    function Table(name, driver){
        this.name = name;
        this.driver = driver;
    }

    Table.prototype.pop = function(callback){
        this.driver.pop(this.name, callback, this);
    }
    Table.prototype.shift = function(callback){
        this.driver.shift(this.name, callback, this);
    }
    Table.prototype.query = function(callback, query, count){
        this.driver.query(this.name, callback, query, count, this)
    }
    Table.prototype.queryByKey = function(key, callback){
        this.driver.queryByKey(this.name, key, callback, this);
    }
    Table.prototype.queryAll = function(callback){
        this.driver.queryAll(this.name, callback, this);
    }
    Table.prototype.insert = function(datas){
        this.driver.insert(this.name, datas);
    }
    Table.prototype.update = function(newVal){
        this.driver.update(this.name, newVal);
    }
    Table.prototype.deleteRow = function(key){
        this.driver.deleteRow(this.name, key);
    }
    Table.prototype.clear = function(){
        this.driver.clear(this.name);
    }
    Table.prototype.fetch = function(callback, keyRange, cursorDirection){
        this.driver.fetch(this.name, callback, keyRange, cursorDirection, this);
    }
    Table.prototype.index = function(indexName, callback, key){
        this.driver.index(this.name, indexName, callback, key, this);
    }
    Table.prototype.multiple = function(indexName, callback, keyRange, cursorDirection){
        this.driver.multiple(this.name, indexName, callback, keyRange, cursorDirection, this);
    }
    Table.prototype.getKeys = function(callback, query, count){
        this.driver.getKeys(this.name, callback, query, count, this);
    }
    Table.prototype.getAllKeys = function(callback){
        this.driver.getAllKeys(this.name, callback, this);
    }
    Table.prototype.count = function(callback, keyRange){
        this.driver.count(this.name, callback, keyRange, this);
    }
    /*****************************************Table********************************************/
    function getDriver(){
        var driver = {},
            win = (typeof window != 'undefined' ? window : global);
        window.innerDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;
        window.DBTransaction = window.IDBTransaction || window.webkitIDBTransaction || window.msIDBTransaction;
        window.DBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange || window.msIDBKeyRange;
        window.INDEXDB_STATUES = {
            CONNECT: {
                ING: 0,
                SUCCESS: 1,
                ERROR: 2,
                CHANGE: 3,
                CLOSE: 4
            },
            DATABASE: {
                ERROR: 0,
                ABORT: 1
            }
        }
        /**
         * 当前数据库
         *  属性 name 名字
         *      version 版本
         *      objectStoreNames 所拥有的表
         * @type {null}
         */
        driver.database = null;
        driver.listener = utils.noop

        driver.addListener = function (listener) {
            this.listener = listener
        }
        /**
         * 打开链接
         * @param options
         */
        driver.open = function(options){
            var _this = this;
            var defaultOptions = {
                name:"",
                version:1,
                tables:[], // {"name":"student", "key":"id","autoIncrement":true, "indexes":[{"name":"aindex", "column":"name", "unique":true}], "datas":[]},
                success:function(){},//数据库打开链接成功
                callback:function(){},//z最后的回调函数
                close:function(){},//数据库关闭
                change:function(){},//版本改变
                error:function(){},//数据库打开链接错误或者数据库错误
                abort:function(){}//数据库中断
            };
            options = utils.isString(options) ? {name:options}:options;

            if(arguments[1]){
                options.version = arguments[1];
            }
            options = utils.extend(defaultOptions, options||{});
            _this.options = options;
            var dbRequest = innerDB.open(options.name, options.version);
            _this.listener(INDEXDB_STATUES.CONNECT.ING)
            dbRequest.onerror = function(e){
                var error = e.target.error;
                console.error(error.message);
                _this.listener(INDEXDB_STATUES.CONNECT.ERROR)
                options.error.call(_this,error);
                options.callback.call(_this,error);
            };
            dbRequest.onsuccess=function(e){
                if(!_this.database)_this.database = e.target.result;
                _this.listener(INDEXDB_STATUES.CONNECT.SUCCESS)
                _this.database.onerror = function(e) {
                    var error = e.target.error;
                    _this.listener(INDEXDB_STATUES.CONNECT.SUCCESS, INDEXDB_STATUES.DATABASE.ERROR)
                    options.error.call(_this,error);
                };
                _this.database.onabort = function(e) {
                    _this.listener(INDEXDB_STATUES.CONNECT.SUCCESS, INDEXDB_STATUES.DATABASE.ABORT)
                    options.abort.call(_this, e.target.error);
                };
                options.success.call(_this, _this.database);
                options.callback.call(_this, _this.database);
            };
            dbRequest.onclose=function(e){
                _this.listener(INDEXDB_STATUES.CONNECT.CLOSE)
                options.close.call(_this, e.target.result);
                options.callback.call(_this, e.target.result);
            }
            dbRequest.onupgradeneeded=function(e){
                _this.listener(INDEXDB_STATUES.CONNECT.SUCCESS)
                if(!_this.database)_this.database = e.target.result;
                for(var j=0;j<options.tables.length;j++){
                    _this.createTable(options.tables[j]);
                }
                options.change.call(_this, _this.database);
                options.callback.call(_this, _this.database);
                console.debug('DB version changed to '+_this.database.version);
            };
        };
        /**
         * 关闭连接
         */
        driver.close = function(){
            if(this.database){
                this.database.close();
                this.database = null;
            }
        };

        /**
         * 是否打开链接
         * @returns {boolean}
         */
        driver.isOpen = function(){
            return this.database != null;
        }
        /**
         * @param options {tables, mode, complete, error} tables
         * @param tables 需要启用事务的表
         * @param mode 事务类型  read write readwrite default
         */
        driver.transaction = function(options){
            var _this = this;
            var defaultOptions = {
                tables : [],
                mode:TransactionMode.READONLY,
                complete:function(){},
                error:function(){},
                callback:function(){}
            };
            if(utils.isString(options) || utils.isArray(options)){
                options = {tables: options};
            }
            if(arguments.length>1){
                options.mode = arguments[1];
            }
            options = utils.extend(defaultOptions, options);
            var transaction = this.database.transaction(options.tables, options.mode);
            transaction.oncomplete = function(e) {
                options.complete.call(_this, e);
            };
            transaction.onerror = function(e) {
                var error = e.target.error;
                console.error(e.target.source.name + " " + error.message);
                options.error.call(_this, error);
            };
            options.callback.call(_this);
            return transaction;
        };

        /**
         * 下一个版本
         * @returns {null|Object|*}
         */
        driver.next = function(){
            return {
                name:this.database ? this.database.name : "default",
                version:this.database ? this.database.version + 1 : 1
            };
        };
        /**
         * 重新打开链接
         * @param options
         */
        driver.reOpen = function(options){
            if(!options || !options.version){
                options = options || {};
                var next = this.next();
                options.name = next.name;
                options.version = next.version;
            }
            this.close();
            this.open(options);
        };

        /**
         * 删除数据库
         * @param database 数据库名称
         */
        driver.dropDatabase = function(database){
            innerDB.deleteDatabase(database);
        };

        /**
         * 创建表
         * table : {"name":"student", "key":"id","autoIncrement":true, "indexes":[{"name":"aindex", "column":"name", "unique":true}]},
         * @param table 表对象 或者表名称 或者表对象数组
         * @param isChange 是否改变版本
         * @param success 当且仅当isChange==true时有用
         * @returns {*}
         */
        driver.createTable = function(table, isChange, success){
            table = utils.isString(table) ? {name:table}:table;
            if(isChange){
                this.reOpen({
                    tables:utils.isArray(table)?table:[table],
                    success:success||function(){}
                });
                return;
            }
            var tables = utils.isArray(table) ? table : [table];

            for(var j=0;j<tables.length;j++){
                var table = tables[j];
                if(this.hasTable(table.name)) continue;
                var objectStore = this.database.createObjectStore(table.name, {keyPath: table["key"]||"id", autoIncrement: !!table["autoIncrement"]});
                if(!utils.isUndefined(table["indexes"])){
                    for(var i=0; i<table["indexes"].length; i++){
                        var index = table["indexes"][i];
                        objectStore.createIndex(index["name"],index["column"],{unique:!!index["unique"], multiEntry:!!index["multiEntry"]}); //创建索引
                        if(table.datas){
                            for(var k=0;k<table.datas.length;k++){
                                objectStore.add(table.datas[k]);
                            }
                        }
                    }
                }
            }
        };
        /**
         * 删除表
         * @param name 表名
         */
        driver.dropTable = function(name){
            this.reOpen({
                change:function(){
                    this.database.deleteObjectStore(name);
                }
            });
        };
        /**
         * 获取所有的表名
         * @returns {*}
         */
        driver.tables = function(){
            return this.database["objectStoreNames"];
        }

        driver.table = function(name){
            return new Table(name, this);
        }

        /**
         * 是否包含某个表
         * @param tableName
         * @returns {*|boolean}
         */
        driver.hasTable = function(tableName){
            return this.tables()&&this.tables().contains(tableName);
        };

        /**
         * 删除索引
         * @param tableName 表名
         * @param indexName 索引名
         */
        driver.dropIndex = function(tableName, indexName){
            var transaction = this.transaction(tableName);
            var objectStore = transaction.objectStore(tableName);
            objectStore.deleteIndex(indexName);
        };
        /**
         * 数据插入
         * @param tableName 表名
         * @param datas 数据内容
         */
        driver.insert = function(tableName, datas){
            var transaction = this.transaction(tableName, TransactionMode.READ_WRITE);
            var objectStore = transaction.objectStore(tableName);
            datas = utils.isArray(datas) ? datas : [datas];
            for(var i=0; i<datas.length; i++){
                objectStore.add(datas[i]);
            }
        };
        driver.update = function(tableName, newVal){
            var transaction = this.transaction(tableName, TransactionMode.READ_WRITE);
            var objectStore = transaction.objectStore(tableName);
            newVal = utils.isArray(newVal) ? newVal : [newVal];
            for(var i=0; i<newVal.length; i++){
                objectStore.put(newVal[i]);
            }
        };

        /**
         * 第一个开始
         */
        driver.pop = function(tableName, callback, target){
            var _this = this;
            var transaction = this.transaction(tableName, TransactionMode.READ_WRITE);
            var objectStore = transaction.objectStore(tableName);
            var request = objectStore.openCursor(null, CursorDirection.NEXT);
            request.onsuccess = function(e){
                var cursor = e.target.result;
                if(cursor) {
                    if(callback)callback.call(target||_this, cursor.key, cursor.value);
                    cursor["continue"]();
                    objectStore["delete"](cursor.key);
                }
            };
            request.onerror = function(e){
                console.error(e.target.error);
                if(callback)callback.call(target||_this, e.target.result);
            }
        };
        /**
         * 末尾开始取值
         */
        driver.shift = function(tableName, callback, target){
            var _this = this;
            var transaction = this.transaction(tableName, TransactionMode.READ_WRITE);
            var objectStore = transaction.objectStore(tableName);
            var request = objectStore.openCursor(CursorDirection.PREV);
            request.onsuccess = function(e){
                var cursor = e.target.result;
                if(cursor) {
                    if(callback)callback.call(target||_this, cursor.key, cursor.value);
                    cursor["continue"]();
                    objectStore["delete"](cursor.key);
                }
            };
            request.onerror = function(e){
                console.error(e.target.error);
                if(callback)callback.call(target||_this, e.target.result);
            }
        };

        /**
         * @param tableName
         * @param callback
         * @param query A value that is or resolves to an KeyRange.
         * @param count Specifies the number of values to return if more than one is found.
         */
        driver.query = function(tableName, callback, query, count, target){
            var _this = this;
            var transaction = this.transaction(tableName);
            var objectStore = transaction.objectStore(tableName);
            var request = objectStore.getAll(query, count);
            request.onsuccess = function(e){
                if(callback)callback.call(target||_this, e.target.result);
            }
            request.onerror = function(e){
                console.error(e.target.error);
                if(callback)callback.call(target||_this, e.target.result);
            }
        };
        /**
         * 根据主键查找
         * @param tableName
         * @param key 主键
         * @param callback
         */
        driver.queryByKey = function(tableName, key, callback, target){
            var _this = this;
            var transaction = this.transaction(tableName);
            var objectStore = transaction.objectStore(tableName);
            var request = objectStore.get(key);
            request.onsuccess = function(e){
                if(callback)callback.call(target||_this, e.target.result);
            }
            request.onerror = function(e){
                if(callback)callback.call(target||_this, e.target.result);
            }
        };
        driver.queryAll = function(tableName, callback, target){
            var _this = this;
            var transaction = this.transaction(tableName);
            var objectStore = transaction.objectStore(tableName);
            var request = objectStore.getAll();
            request.onsuccess = function(e){
                if(callback)callback.call(target||_this, e.target.result);
            }
            request.onerror = function(e){
                console.error(e.target.error);
                if(callback)callback.call(target||_this, e.target.result);
            }
        };

        /**
         * 使用游标循环
         * @param tableName
         * @param callback
         */
        driver.fetch = function(tableName, callback, keyRange, cursorDirection, target){
            var _this = this;
            var transaction = this.transaction(tableName);
            var objectStore = transaction.objectStore(tableName);
            var request = keyRange?objectStore.openCursor():objectStore.openCursor(keyRange, cursorDirection || CursorDirection.NEXT);
            request.onsuccess = function(e){
                var cursor = e.target.result;
                if(cursor) {
                    if(callback)callback.call(target||_this, cursor.key, cursor.value);
                    cursor["continue"]();
                }
            };
            request.onerror = function(e){
                console.error(e.target.error);
                if(callback)callback.call(target||_this, e.target.result);
            }
        };
        /**
         * 使用索引游标查找
         * @param tableName
         * @param indexName
         * @param key allKeys > getAllKeys()
         * @param callback
         */
        driver.index = function(tableName, indexName, callback, key, target){
            var _this = this;
            var transaction = this.transaction(tableName);
            var objectStore = transaction.objectStore(tableName);
            var index = objectStore.index(indexName);
            var request = key == KeyRange.ALL_KEYS ? index.getAllKeys() : key ? index.get(key) : index.getAll();
            request.onsuccess=function(e){
                if(callback)callback.call(target||_this, e.target.result);
            }
            request.onerror = function(e){
                console.error(e.target.error);
                if(callback)callback.call(target||_this, e.target.result);
            }
        };
        driver.multiple = function(tableName, indexName, callback, keyRange, cursorDirection, target){
            var _this = this;
            var transaction = this.transaction(tableName);
            var objectStore = transaction.objectStore(tableName);
            var index = objectStore.index(indexName);
            var cursor = index.openCursor(keyRange, cursorDirection || CursorDirection.NEXT);
            cursor.onsuccess = function(e){
                var cursor= e.target.result;
                if(cursor){
                    callback.call(target||_this, cursor.key ,cursor.value);
                    cursor["continue"]();
                }
            }
            cursor.onerror = function(e){
                console.error(e.target.error);
                if(callback)callback.call(target||_this, e.target.result);
            }
        };

        driver.getKeys = function(tableName, callback, query, count, target){
            var _this = this;
            var transaction = this.transaction(tableName);
            var objectStore = transaction.objectStore(tableName);
            var request = query ? objectStore.getAllKeys(query, count) : objectStore.getAllKeys();
            request.onsuccess = function(e){
                if(callback)callback.call(target||_this, e.target.result);
            }
            request.onerror = function(e){
                console.error(e.target.error);
                if(callback)callback.call(target||_this, e.target.result);
            }
        }

        /**
         * @param tableName
         * @param query A value that is or resolves to an KeyRange.
         * @param count Specifies the number of values to return if more than one is found.
         */
        driver.getAllKeys = function(tableName, callback, target){
            this.getKeys(tableName, callback, null, null, target);
        };

        driver.deleteRow = function(tableName, key){
            var transaction = this.transaction(tableName, TransactionMode.READ_WRITE);
            var objectStore = transaction.objectStore(tableName);
            objectStore["delete"](key);
        };
        driver.clear = function(tableName){
            var transaction = this.transaction(tableName, TransactionMode.READ_WRITE);
            var objectStore = transaction.objectStore(tableName);
            objectStore.clear();
        };

        /**
         * 统计
         * @param tableName 表名
         * @param keyRange 范围或者key值
         *  eg: count("asd") or count(KeyRange.eq("sdf")) or count()
         *  @see KeyRange
         * @param callback 回调
         */
        driver.count = function(tableName, callback, keyRange, target){
            var _this = this;
            var transaction = this.transaction(tableName);
            var objectStore = transaction.objectStore(tableName);
            var countRequest = keyRange ? objectStore.count(keyRange) : objectStore.count();
            countRequest.onsuccess = function(e) {
                if(callback)callback.call(target||_this, e.target.result);
            }
            countRequest.onerror = function(e) {
                console.error(e.target.error);
                if(callback)callback.call(target||_this, e.target.result);
            }
        };

        return driver;
    }

    function compress(obj){
        if(utils.isArray(obj)){
            for(var i=0; i<obj.length; i++){
                obj[i] = compress(obj[i]);
            }
        }else if(utils.isObject(obj)){
            for(var prop in obj){
                obj[prop] = compress(obj[prop]);
            }
        }else if(utils.isFunction(obj)){
            obj = obj.toString();
        }else{
            if(obj && obj.length && obj.length > 100){
                obj = obj.substring(0, 100);
            }
        }
        return obj;
    }

    function DriverProxy(){
        this.sysDriver = getDriver();
        this.engine = getDriver();
        this.logEnable = false;
        this.logInterval = 60*1000;
        this.logExpire = 0;

        this.logs = [];
        this.version = "1.0";
        this.enable = true;
        this.disable = !this.enable;
    }

    DriverProxy.prototype.log = function(obj){
        var log = {};
        log.timestamp = Date.now();
        var database = proxy.engine.database;
        if(database){
            this.database = database.name;
            this.version = database.version;
        }
        log.expire = this.expire;
        if(obj){
            if(utils.isObject(obj)){
                for(var prop in obj){
                    log[prop] = compress(clone(obj[prop]));
                }
            }else if(utils.isArray(obj)){
                log["record"] = compress(obj);
            }else if(arguments.length > 1){
                log["record"] = compress(arguments);
            }
        }
        this.logs.push(log);
    }

    DriverProxy.prototype.init = function(callback){
        var proxy = this;
        proxy.sysDriver.open({
            name:"sys_db",
            tables:[
                //系统参数
                {"name":"sys_params", "key":"key"},
                {"name":"sys_db_record", "key":"database"},
                {"name":"sys_logs", "key":"id", "autoIncrement":true, "indexes":[{"name":"logIndex", "column":"timestamp", "unique":true}]}
            ],
            success:function(){
                var _this = this;
                if(callback)callback.apply(_this);
                if(proxy.logEnable){
                    setInterval(function(){
                        while(proxy.logs.length > 0){
                            _this.insert("sys_logs", proxy.logs.shift())
                        }
                    }, proxy.logInterval);
                }
            }
        });
    }
    DriverProxy.prototype.close = function(){
        this.sysDriver.close();
        this.sysDriver = null;
        this.engine.close();
        this.engine = null;
    }
    DriverProxy.prototype.invoke = function(method){
        var self = this
        if(self.disable){
            console.warn("Your browser unsuport this operate!");
            return;
        }
        var args = Array.prototype.slice.call(arguments).slice(1);
        if(self.logEnable){
            self.log({method:method, args:args, expire:self.logExpire});
        }
        if(self.debug){
            console.debug("execute method:["+method+"], arguments:" + JSON.stringify(args));
            console.trace();
        }
        self.before(method, args);
        self.record(method);
        var result = self[method] ? self[method].apply(self, args) :self.engine[method].apply(self.engine, args);
        self.after(method, args);
        return result;
    }

    DriverProxy.prototype.record = function(method){
        if(["createTable", "dropTable"].contains(method)){
            this.sysDriver.update("sys_params", {"key":"using_db", "value":this.engine.database.name, "version":this.engine.database.version+1});
        }
    }

    DriverProxy.prototype.open = function(options){
        var _this = this;
        this.init(function(){
            options = options || {};
            options = utils.isString(options) ? {name:options}:options;
            if(arguments[1]){
                options.version = arguments[1];
            }

            this.queryByKey(!options.name?"sys_params":"sys_db_record", !options.name?"using_db":options.name, function(result){
                if(result){//如果存在使用中的database  直接打开
                    options.name = !options.name?result.value:result.database;
                    options.version = Math.max(options.version || 1, result.version || 1);
                }
                console.assert(options.name, "database is required!");
                _this.engine.open(options);
                this.update("sys_db_record", {"database":options.name, "version":options.version});
                this.update("sys_params", {"key":"using_db", "value":options.name, "version":options.version});
            });
        });
    }
    DriverProxy.prototype.before = function(){}
    DriverProxy.prototype.after = function(){}

    var proxy = new DriverProxy();

    window.addEventListener("unload", function(){
        proxy.close();
    });

    return {
        log:function(enable, interval, expire){
            proxy.logEnable = enable != false;
            proxy.logInterval = interval || proxy.logInterval;
            proxy.logExpire = expire || proxy.logExpire;
            return this;
        },
        addListener: function (listener) {
            proxy.engine.addListener(listener)
        },
        debug:function(enable){
            proxy.debug = enable != false;
            return this;
        },
        /**
         * 打开链接
         * @param options
         */
        open : function(options){
            proxy.invoke("open", options);
            return this;
        },
        isOpen : function(){
            return proxy.invoke("isOpen");
        },
        /**
         * 删除数据库
         * @param database 数据库名称
         */
        dropDatabase : function(database){
            proxy.invoke("dropDatabase", database);
            return this;
        },

        /**
         * 创建表
         * table : {"name":"student", "key":"id","autoIncrement":true, "indexes":[{"name":"aindex", "column":"name", "unique":true}]},
         * @param table 表对象 或者表名称 或者表对象数组
         * @param isChange 是否改变版本
         * @param success 当且仅当isChange:=true时有用
         * @returns {*}
         */
        createTable : function(table, success){
            proxy.invoke("createTable", table, true, success);
            return this;
        },
        /**
         * 删除表
         * @param name 表名
         */
        dropTable : function(name){
            proxy.invoke("dropTable", name);
            return this;
        },
        /**
         * 获取所有的表名
         * @returns {*}
         */
        getTables : function(){
            return proxy.invoke("tables");
        },
        getTable : function(name){
            return proxy.invoke("table", name);
        },
        /**
         * 是否包含某个表
         * @param tableName
         * @returns {*|boolean}
         */
        hasTable : function(tableName){
            return proxy.invoke("hasTable", tableName);
        },
        /**
         * 删除索引
         * @param tableName 表名
         * @param indexName 索引名
         */
        dropIndex : function(tableName, indexName){
            proxy.invoke("dropIndex", tableName, indexName);
            return this;
        },
        /**
         * 数据插入
         * @param tableName 表名
         * @param datas 数据内容
         */
        insert : function(tableName, datas){
            proxy.invoke("insert", tableName, datas);
            return this;
        },
        update : function(tableName, newVal){
            proxy.invoke("update", tableName, newVal);
            return this;
        },
        /**
         * 第一个开始
         */
        pop : function(tableName, callback){
            proxy.invoke("pop", tableName, callback);
            return this;
        },
        /**
         * 末尾开始取值
         */
        shift : function(tableName, callback){
            proxy.invoke("shift", tableName, callback);
            return this;
        },
        /**
         * @param tableName
         * @param callback
         * @param query A value that is or resolves to an KeyRange.
         * @param count Specifies the number of values to return if more than one is found.
         */
        query : function(tableName, callback, query, count){
            proxy.invoke("query", tableName, callback, query, count);
            return this;
        },
        /**
         * 根据主键查找
         * @param tableName
         * @param key 主键
         * @param callback
         */
        queryByKey : function(tableName, key, callback){
            proxy.invoke("queryByKey", tableName, key, callback);
            return this;
        },
        queryAll : function(tableName, callback){
            proxy.invoke("queryAll", tableName, callback);
            return this;
        },
        /**
         * 使用游标循环
         * @param tableName
         * @param callback
         */
        fetch : function(tableName, callback, keyRange, cursorDirection){
            proxy.invoke("fetch", tableName, callback, keyRange, cursorDirection);
            return this;
        },
        /**
         * 使用索引游标查找
         * @param tableName
         * @param indexName
         * @param key
         * @param callback
         */
        index : function(tableName, indexName, callback, key){
            proxy.invoke("index", tableName, indexName, callback, key);
            return this;
        },
        multiple : function(tableName, indexName, callback, keyRange, cursorDirection){
            proxy.invoke("multiple", tableName, indexName, callback, keyRange, cursorDirection);
            return this;
        },
        /**
         * @param tableName
         * @param query A value that is or resolves to an KeyRange.
         * @param count Specifies the number of values to return if more than one is found.
         */
        getAllKeys : function(tableName, callback, query, count){
            proxy.invoke("getAllKeys", tableName, callback, query, count);
            return this;
        },
        deleteRow : function(tableName, key){
            proxy.invoke("deleteRow", tableName, key);
            return this;
        },
        clear : function(tableName){
            proxy.invoke("clear", tableName);
            return this;
        },
        /**
         * 统计
         * @param tableName 表名
         * @param keyRange 范围或者key值
         *  eg: count("asd") or count(KeyRange.eq("sdf")) or count()
         *  @see KeyRange
         * @param callback 回调
         */
        count : function(tableName, callback, keyRange){
            proxy.invoke("count", tableName, callback, keyRange);
            return this;
        }
    };
});